define(['./mbDomUtils', './mbBaseModels', 'AABB'], function (mbDomUtils, mbBaseModels, AABB) {

    var $ml = {};

//======================================================================================================================
//region Basic Polylines
//======================================================================================================================

    function pointDist(p1, p2) {
        var x1 = p1[0] || p1.x;
        var y1 = p1[1] || p1.y;
        var x2 = p2[0] || p2.x;
        var y2 = p2[1] || p2.y;
        return Math.hypot(x1 - x2, y1 - y2);
    }

    // Points: [PointLike]
    function MBMapPolyline(points, opts) {

        var self = this;

        // autoClose === _isPolygon
        this._isPolygon = this._isPolygon || false;
        this._hasExtrusion = this._hasExtrusion || false;

        this._uuid = opts._uuid = opts._uuid || H.fastUuid().toLowerCase();
        this._points = points;

        this.checkAutoClose = function () {
            if (!this._isPolygon) return;
            var points = self._points;
            // if not closed, close it
            if (points.length > 1) {
                var first = points[0];
                var last = points[points.length - 1];
                var dist = pointDist(first, last);
                /*
                 * 0.000001 * 111km = 0.1m
                 */
                if (dist > 0.000001) {
                    points.push([first[0] || first.x, first[1] || first.y]);
                }
            }
        };

        this.checkAutoClose(points);

        /*
         * points: [[lng, lat], [lng, lat], ...]
         */

        self.sourceData = self.sourceData || {
            'type': 'Feature',
            'properties': {},
            'geometry': {
                'type': 'LineString',
                'coordinates': this._points
            }
        };
        self.sourceOpts = self.sourceOpts || {
            'type': 'geojson',
            'data': self.sourceData
        };
        self.layerOpts = self.layerOpts || {
            // 'id': layerId, // will be filled in AbstractLayer
            'type': 'line',
            // 'source': layerId,
            'layout': {
                'line-join': 'round',
                'line-cap': 'round',
                'line-miter-limit': 2,
                'line-round-limit': 1.05, // takes effect when line-join is round
                // 'line-sort-key': 0, // zindex like property
                'visibility': 'visible',
            },
            'paint': {
                'line-blur': 0,
                'line-color': opts.strokeColor,
                /*
                 * TODO: when in solid mode, do not provide the key here, do not pass in `null`
                 */
                // 'line-dasharray': (opts.strokeStyle !== 'dashed') ? undefined : (opts.strokeDasharray || [10, 5]), // [1,5]
                'line-gap-width': 0,
                // 'line-gradient': '#000',
                'line-offset': 0,
                'line-opacity': opts.strokeOpacity !== undefined ? opts.strokeOpacity : 1,
                // 'line-pattern': null, // optional ResolvedImage
                'line-translate': [0, 0],
                'line-translate-anchor': 'map',
                'line-width': opts.strokeWeight || 5,
            }
        };

        this._readCoordinatesFromFeature = this._readCoordinatesFromFeature || function (feature) {
            return feature.geometry.coordinates;
        };

        this._writeCoordinatesToFeature = this._writeCoordinatesToFeature || function (feature, coords) {
            feature.geometry.coordinates = coords;
        };

        /*
         * Exposed Interfaces
         */

        this.getPath = function () {
            return [].concat(this._points);
        };

        this.setPath = function (points) {

            this.checkAutoClose(points);

            self._points = points;
            self._writeCoordinatesToFeature(self.sourceData, points);
            if (this.currMap) {
                this.currMap.getSource(this._uuid).setData(self.sourceData);
            }
        };

        this.getLength = function () {
            var points = self.sourceData.geometry.coordinates || [];
            if (!points || points.length < 2) return 0;
            var left = new mapboxgl.LngLat(points[0][0], points[0][1]);
            var right = new mapboxgl.LngLat();
            var totalDist = 0;
            for (var i = 1; i < points.length; i++) {
                var next = points[i];
                right.lng = next[0];
                right.lat = next[1];
                totalDist += left.distanceTo(right);

                // swap left/right for next
                var p = left;
                left = right;
                right = p;
            }
            return totalDist;
        };

        mbBaseModels.AbstractLayer.call(this, opts, self.sourceOpts, self.layerOpts);
    }

    MBMapPolyline.prototype = Object.assign(Object.create(mbBaseModels.AbstractLayer.prototype), {
        constructor: MBMapPolyline
    });

    /*
     * 2000 points per route
     * 100 routes reaches the limit
     */
    function addOutdoorPolyline(points, opts) {
        return new MBMapPolyline(points, opts);
    }

//======================================================================================================================
//endregion
//======================================================================================================================

    $ml.MBMapPolyline = MBMapPolyline;
    $ml.addOutdoorPolyline = addOutdoorPolyline;

    return $ml;

});